<template>
  <view class="calendar-component">
    <view class="calendar-header">
      <view class="view-switch">
        <text
          :class="['switch-item', { active: viewMode === 'month' }]"
          @tap="switchView('month')"
          >月</text
        >
        <text
          :class="['switch-item', { active: viewMode === 'week' }]"
          @tap="switchView('week')"
          >周</text
        >
      </view>
      <view class="header-controls">
        <view class="year-control">
          <i
            class="iconfont icon-youjiantou1"
            style="font-size: 32rpx"
            @tap="changeYear(-1)"
          >
          </i>
          <text class="current-year">{{ displayYearMonth.year }}年</text>
          <text
            class="iconfont icon-youjiantou"
            style="font-size: 32rpx"
            @tap="changeYear(1)"
          ></text>
        </view>
        <view class="month-control">
          <i
            class="iconfont icon-youjiantou1"
            style="font-size: 32rpx"
            @tap="handleMonthChange(-1)"
          >
          </i>
          <text class="current-month">{{ displayYearMonth.month }}月</text>
          <text
            class="iconfont icon-youjiantou"
            style="font-size: 32rpx"
            @tap="handleMonthChange(1)"
          ></text>
        </view>
      </view>
      <view class="weekday-header">
        <text v-for="day in weekDays" :key="day">{{ day }}</text>
      </view>
    </view>
    <view class="calendar-body">
      <template v-if="viewMode === 'month'">
        <view class="days-grid">
          <view
            v-for="(day, idx) in currentMonthDays"
            :key="'current-' + idx"
            class="day-cell"
            :class="{
              'is-selected': isSelected(day),
              'is-range-start': isRangeStart(day),
              'is-range-end': isRangeEnd(day),
              'is-in-range': isInRange(day),
              'is-today': isToday(day),
              'is-disabled': isDisabled(day),
              'is-empty': !day,
              'is-other-month': day && day.isOtherMonth,
            }"
            @tap="handleDayClick(day)"
          >
            <text :class="{ 'other-month': day && day.isOtherMonth }">
              {{ day ? day.date : "" }}
            </text>
            <view v-if="isSelected(day)" class="dot"></view>
          </view>
        </view>
      </template>
      <template v-else>
        <view class="days-grid week-mode">
          <view
            v-for="(day, idx) in displayDays"
            :key="idx"
            class="day-cell"
            :class="{
              'is-selected': isSelected(day),
              'is-range-start': isRangeStart(day),
              'is-range-end': isRangeEnd(day),
              'is-in-range': isInRange(day),
              'is-today': isToday(day),
              'is-disabled': isDisabled(day),
              'is-empty': !day,
              'is-other-month': day && day.isOtherMonth,
            }"
            @tap="handleDayClick(day)"
          >
            <text :class="{ 'other-month': day && day.isOtherMonth }">
              {{ day ? day.date : "" }}
            </text>
            <view v-if="isSelected(day)" class="dot"></view>
          </view>
        </view>
      </template>
    </view>
  </view>
</template>

<script>
import Taro from "@tarojs/taro";
import { formatDate, isDateEqual, isDateBefore, isDateAfter } from "./utils";

/**
 * @typedef {Object} DayInfo
 * @property {number} date - The day of the month (1-31)
 * @property {number} month - The month (1-12)
 * @property {number} year - The year
 * @property {boolean} isOtherMonth - Whether this day belongs to another month
 */

/**
 * 节流函数
 * @param {Function} fn 需要节流的函数
 * @param {number} delay 节流延迟时间
 * @returns {Function} 节流后的函数
 */
function throttle(fn, delay) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= delay) {
      fn.apply(this, args);
      lastTime = now;
    }
  };
}

export default {
  name: "CalendarComponent",

  props: {
    mode: {
      type: String,
      default: "single",
      validator: (value) => ["single", "multiple", "range"].includes(value),
    },
    defaultViewMode: {
      type: String,
      default: "month",
      validator: (value) => ["month", "week"].includes(value),
    },
    value: {
      type: [Date, Array],
      default: () => new Date(),
    },
    minDate: {
      type: Date,
      default: () => new Date(1900, 0, 1),
    },
    maxDate: {
      type: Date,
      default: () => new Date(2100, 11, 31),
    },
    disabledDates: {
      type: Array,
      default: () => [],
    },
    height: {
      type: String,
      default: "1200rpx",
    },
  },

  data() {
    const today = new Date();
    return {
      weekDays: ["日", "一", "二", "三", "四", "五", "六"],
      selectedDates: [],
      rangeStart: null,
      rangeEnd: null,
      currentYear: today.getFullYear(),
      currentMonth: today.getMonth() + 1,
      viewMode: this.defaultViewMode,
      currentWeekIndex: 0,
      loading: false,
      loadLock: false,
      monthsToLoad: 3,
      maxMonths: 12,
      scrollTop: 0,
      lastScrollTop: 0,
      scrollThreshold: 100,
      throttleDelay: 500,
    };
  },

  computed: {
    /** @returns {DayInfo[]} */
    displayDays() {
      if (this.viewMode === "week") {
        const allDays = this.currentMonthDays;
        const weekStart = this.currentWeekIndex * 7;
        return allDays.slice(weekStart, weekStart + 7);
      }
      return this.currentMonthDays;
    },

    displayYearMonth() {
      if (
        this.viewMode === "week" &&
        this.displayDays &&
        this.displayDays.length > 0
      ) {
        const firstDay = this.displayDays[0];
        const lastDay = this.displayDays[6];

        if (firstDay.month !== lastDay.month) {
          return {
            year:
              firstDay.year !== lastDay.year
                ? `${firstDay.year}-${lastDay.year}`
                : `${firstDay.year}`,
            month: `${firstDay.month}-${lastDay.month}`,
          };
        }

        return {
          year: firstDay.year,
          month: firstDay.month,
        };
      }

      return {
        year: this.currentYear,
        month: this.currentMonth,
      };
    },

    /** @returns {DayInfo[]} */
    currentMonthDays() {
      return this.generateMonthDays(this.currentYear, this.currentMonth);
    },
  },

  watch: {
    value: {
      handler(newVal) {
        this.initSelection(newVal);
      },
      immediate: true,
    },
  },

  created() {
    this.initSelection(this.value);
    this.generateCurrentMonthDays();
    // 创建节流后的loadMoreMonths方法
    this.throttledLoadMoreMonths = throttle(
      this.loadMoreMonths,
      this.throttleDelay
    );
  },

  methods: {
    initSelection(value) {
      if (this.mode === "range") {
        if (Array.isArray(value) && value.length === 2) {
          [this.rangeStart, this.rangeEnd] = value;
        } else {
          this.rangeStart = null;
          this.rangeEnd = null;
        }
      } else if (this.mode === "single") {
        this.selectedDates = [value];
      } else {
        this.selectedDates = Array.isArray(value) ? value : [value];
      }
    },

    generateCurrentMonthDays() {
      const firstDay = new Date(this.currentYear, this.currentMonth - 1, 1);
      const lastDay = new Date(this.currentYear, this.currentMonth, 0);
      const daysInMonth = lastDay.getDate();
      const firstDayWeekDay = firstDay.getDay();

      // 获取上个月的最后几天
      const prevMonthLastDay = new Date(
        this.currentYear,
        this.currentMonth - 1,
        0
      );
      const prevMonthDays = [];
      for (let i = firstDayWeekDay - 1; i >= 0; i--) {
        const day = prevMonthLastDay.getDate() - i;
        const month = this.currentMonth - 1;
        const year = this.currentYear;
        prevMonthDays.push({
          date: day,
          month: month === 0 ? 12 : month,
          year: month === 0 ? year - 1 : year,
          isOtherMonth: true,
        });
      }

      // 当前月的天数
      const currentMonthDays = Array.from({ length: daysInMonth }, (_, i) => ({
        date: i + 1,
        month: this.currentMonth,
        year: this.currentYear,
        isOtherMonth: false,
      }));

      // 下个月的开始几天
      const remainingDays = 42 - (prevMonthDays.length + daysInMonth); // 保持6行7列的格式
      const nextMonthDays = Array.from({ length: remainingDays }, (_, i) => {
        const month = this.currentMonth + 1;
        const year = this.currentYear;
        return {
          date: i + 1,
          month: month === 13 ? 1 : month,
          year: month === 13 ? year + 1 : year,
          isOtherMonth: true,
        };
      });

      this.currentMonthDays = [
        ...prevMonthDays,
        ...currentMonthDays,
        ...nextMonthDays,
      ];
    },

    handleMonthChange(delta) {
      if (this.viewMode === "week") {
        this.changeWeek(delta);
        return;
      }

      let newMonth = this.currentMonth + delta;
      let newYear = this.currentYear;

      if (newMonth > 12) {
        newMonth = 1;
        newYear++;
      } else if (newMonth < 1) {
        newMonth = 12;
        newYear--;
      }

      // 检查是否超出范围
      const newDate = new Date(newYear, newMonth - 1, 1);
      if (newDate < this.minDate || newDate > this.maxDate) return;

      this.currentYear = newYear;
      this.currentMonth = newMonth;
    },

    changeYear(delta) {
      const newYear = this.currentYear + delta;
      const newDate = new Date(newYear, this.currentMonth - 1, 1);

      // 检查是否超出范围
      if (newDate < this.minDate || newDate > this.maxDate) return;

      this.currentYear = newYear;
      this.generateCurrentMonthDays();
    },

    /**
     * @param {DayInfo | null} day
     */
    handleDayClick(day) {
      if (!day || this.isDisabled(day)) return;

      const date = new Date(day.year, day.month - 1, day.date);

      if (this.mode === "range") {
        if (!this.rangeStart || (this.rangeStart && this.rangeEnd)) {
          this.rangeStart = date;
          this.rangeEnd = null;
        } else {
          if (isDateBefore(date, this.rangeStart)) {
            this.rangeEnd = this.rangeStart;
            this.rangeStart = date;
          } else {
            this.rangeEnd = date;
          }
          this.$emit("dateChange", [this.rangeStart, this.rangeEnd]);
        }
      } else if (this.mode === "single") {
        this.selectedDates = [date];
        this.$emit("dateChange", date);
      } else {
        const index = this.selectedDates.findIndex((d) => isDateEqual(d, date));
        if (index === -1) {
          this.selectedDates.push(date);
        } else {
          this.selectedDates.splice(index, 1);
        }
        this.$emit("dateChange", this.selectedDates);
      }
    },

    isSelected(day) {
      if (!day) return false;
      const date = new Date(day.year, day.month - 1, day.date);
      return this.selectedDates.some((d) => isDateEqual(d, date));
    },

    isRangeStart(day) {
      if (!day || !this.rangeStart) return false;
      const date = new Date(day.year, day.month - 1, day.date);
      return isDateEqual(date, this.rangeStart);
    },

    isRangeEnd(day) {
      if (!day || !this.rangeEnd) return false;
      const date = new Date(day.year, day.month - 1, day.date);
      return isDateEqual(date, this.rangeEnd);
    },

    isInRange(day) {
      if (!day || !this.rangeStart || !this.rangeEnd) return false;
      const date = new Date(day.year, day.month - 1, day.date);
      return (
        isDateAfter(date, this.rangeStart) && isDateBefore(date, this.rangeEnd)
      );
    },

    isToday(day) {
      if (!day) return false;
      const today = new Date();
      return (
        day.year === today.getFullYear() &&
        day.month === today.getMonth() + 1 &&
        day.date === today.getDate()
      );
    },

    isDisabled(day) {
      if (!day) return true;
      const date = new Date(day.year, day.month - 1, day.date);
      return (
        date < this.minDate ||
        date > this.maxDate ||
        this.disabledDates.some((d) => isDateEqual(d, date))
      );
    },

    async loadMoreMonths(direction) {
      if (this.loading || this.loadLock) return;

      this.loading = true;
      this.loadLock = true;

      try {
        let newMonths = [];
        const currentScrollHeight =
          document.querySelector(".calendar-scroll")?.scrollHeight || 0;

        if (direction === "forward") {
          const lastMonth = this.monthList[this.monthList.length - 1];
          const startDate = new Date(lastMonth.year, lastMonth.month, 1);
          const endDate = new Date(
            startDate.getFullYear(),
            startDate.getMonth() + this.monthsToLoad,
            1
          );
          newMonths = this.generateMonthsBetween(startDate, endDate);

          if (this.monthList.length + newMonths.length > this.maxMonths) {
            this.monthList = this.monthList.slice(newMonths.length);
          }

          this.monthList.push(...newMonths);
        } else {
          const firstMonth = this.monthList[0];
          const endDate = new Date(firstMonth.year, firstMonth.month - 1, 1);
          const startDate = new Date(
            endDate.getFullYear(),
            endDate.getMonth() - this.monthsToLoad,
            1
          );
          newMonths = this.generateMonthsBetween(startDate, endDate);

          if (this.monthList.length + newMonths.length > this.maxMonths) {
            this.monthList = this.monthList.slice(0, -newMonths.length);
          }

          this.monthList.unshift(...newMonths);

          await this.$nextTick();
          const newScrollHeight =
            document.querySelector(".calendar-scroll")?.scrollHeight || 0;
          const heightDiff = newScrollHeight - currentScrollHeight;
          if (heightDiff > 0) {
            this.scrollTop = this.lastScrollTop + heightDiff;
          }
        }

        await this.$nextTick();
      } catch (error) {
        console.error("加载月份数据失败:", error);
      } finally {
        setTimeout(() => {
          this.loading = false;
          this.loadLock = false;
        }, 200);
      }
    },

    generateMonthsBetween(start, end) {
      const months = [];
      const current = new Date(start);

      while (current <= end) {
        months.push({
          year: current.getFullYear(),
          month: current.getMonth() + 1,
          days: generateMonthDays(current.getFullYear(), current.getMonth()),
        });
        current.setMonth(current.getMonth() + 1);
      }

      return months;
    },

    switchView(mode) {
      if (this.viewMode === mode) return;
      this.viewMode = mode;

      if (mode === "week") {
        // 找到包含今天的周
        const today = new Date();
        const todayIndex = this.currentMonthDays.findIndex(
          (day) => day && this.isToday(day)
        );
        if (todayIndex !== -1) {
          this.currentWeekIndex = Math.floor(todayIndex / 7);
        } else {
          this.currentWeekIndex = 0;
        }
      }
    },

    changeWeek(delta) {
      if (this.viewMode !== "week") return;

      const newIndex = this.currentWeekIndex + delta;
      const totalWeeks = Math.ceil(this.currentMonthDays.length / 7);

      if (newIndex >= totalWeeks) {
        // 切换到下个月第一周
        this.currentWeekIndex = 0;
        let newMonth = this.currentMonth + 1;
        let newYear = this.currentYear;

        if (newMonth > 12) {
          newMonth = 1;
          newYear++;
        }

        this.currentMonth = newMonth;
        this.currentYear = newYear;
        this.generateCurrentMonthDays();
      } else if (newIndex < 0) {
        // 切换到上个月最后一周
        let newMonth = this.currentMonth - 1;
        let newYear = this.currentYear;

        if (newMonth < 1) {
          newMonth = 12;
          newYear--;
        }

        this.currentMonth = newMonth;
        this.currentYear = newYear;
        this.generateCurrentMonthDays();
        this.currentWeekIndex = Math.ceil(this.currentMonthDays.length / 7) - 1;
      } else {
        this.currentWeekIndex = newIndex;
      }
    },

    /**
     * @param {number} year
     * @param {number} month
     * @returns {DayInfo[]}
     */
    generateMonthDays(year, month) {
      const firstDay = new Date(year, month - 1, 1);
      const lastDay = new Date(year, month, 0);
      const daysInMonth = lastDay.getDate();
      const firstDayWeekDay = firstDay.getDay();

      // 获取上个月的最后几天
      const prevMonthLastDay = new Date(year, month - 1, 0);
      const prevMonthDays = [];
      for (let i = firstDayWeekDay - 1; i >= 0; i--) {
        const day = prevMonthLastDay.getDate() - i;
        const prevMonth = month - 1;
        const prevYear = prevMonth === 0 ? year - 1 : year;
        prevMonthDays.push({
          date: day,
          month: prevMonth === 0 ? 12 : prevMonth,
          year: prevYear,
          isOtherMonth: true,
        });
      }

      // 当前月的天数
      const currentMonthDays = Array.from({ length: daysInMonth }, (_, i) => ({
        date: i + 1,
        month: month,
        year: year,
        isOtherMonth: false,
      }));

      // 下个月的开始几天
      const remainingDays = 42 - (prevMonthDays.length + daysInMonth);
      const nextMonthDays = Array.from({ length: remainingDays }, (_, i) => {
        const nextMonth = month + 1;
        const nextYear = nextMonth === 13 ? year + 1 : year;
        return {
          date: i + 1,
          month: nextMonth === 13 ? 1 : nextMonth,
          year: nextYear,
          isOtherMonth: true,
        };
      });

      return [...prevMonthDays, ...currentMonthDays, ...nextMonthDays];
    },
  },

  beforeDestroy() {
    // 清理节流函数
    this.throttledLoadMoreMonths = null;
  },
};
</script>

<style lang="scss">
.calendar-component {
  width: 100%;
  background-color: #fff;
  border-radius: 12rpx;
  overflow: hidden;
}

.calendar-header {
  padding: 20rpx;
  border-bottom: 1rpx solid #eee;
}

.header-controls {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20rpx;
  gap: 40rpx;
}

.year-control,
.month-control {
  display: flex;
  align-items: center;
  gap: 20rpx;
}

.current-year,
.current-month {
  font-size: 32rpx;
  font-weight: bold;
  min-width: 120rpx;
  text-align: center;
}

.arrow {
  font-size: 32rpx;
  color: $primary-color;
  padding: 10rpx 20rpx;
  cursor: pointer;

  &:active {
    opacity: 0.7;
  }
}

.weekday-header {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  padding: 10rpx 0;
  border-bottom: 1rpx solid #eee;
}

.weekday-header text {
  font-size: 28rpx;
  color: #666;
}

.calendar-body {
  padding: 10rpx;
}

.days-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4rpx;

  &.week-mode {
    grid-template-rows: 1fr;
    height: 100rpx;
  }
}

.day-cell {
  position: relative;
  height: 80rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28rpx;
  color: #333;

  &.is-other-month text {
    color: #999;
  }

  &.is-selected {
    background-color: $primary-color;
    color: #fff !important;
    border-radius: 8rpx;
  }

  &.is-range-start {
    background-color: $primary-color;
    color: #fff;
    border-radius: 8rpx 0 0 8rpx;
  }

  &.is-range-end {
    background-color: $primary-color;
    color: #fff;
    border-radius: 0 8rpx 8rpx 0;
  }

  &.is-in-range {
    background-color: rgba(32, 128, 240, 0.1);
    color: $primary-color;
  }

  &.is-today {
    font-weight: bold;
    color: $primary-color;
  }

  &.is-disabled {
    color: #999;
    opacity: 0.4;
    cursor: not-allowed;
  }

  &.is-empty {
    pointer-events: none;
  }
}

.dot {
  position: absolute;
  bottom: 8rpx;
  width: 8rpx;
  height: 8rpx;
  border-radius: 50%;
  background-color: #fff;
}

.view-switch {
  display: flex;
  justify-content: center;
  margin-bottom: 20rpx;
  gap: 20rpx;
}

.switch-item {
  padding: 10rpx 30rpx;
  font-size: 28rpx;
  color: #666;
  border-radius: 8rpx;
  background: #f5f5f5;

  &.active {
    color: #fff;
    background: $primary-color;
  }
}

[data-theme="dark"] {
  .calendar-component {
    background-color: #1a1a1a;
  }

  .calendar-header {
    border-bottom-color: #333;
  }

  .weekday-header {
    border-bottom-color: #333;
  }

  .weekday-header text {
    color: #999;
  }

  .day-cell {
    color: #fff;

    &.is-other-month text {
      color: #666;
    }

    &.is-selected,
    &.is-range-start,
    &.is-range-end {
      background-color: $primary-color;
    }

    &.is-in-range {
      background-color: rgba(64, 158, 255, 0.1);
    }

    &.is-today {
      color: $primary-color;
    }

    &.is-disabled {
      color: #666;
    }
  }

  .switch-item {
    background: #333;
    color: #999;

    &.active {
      background: $primary-color;
      color: #fff;
    }
  }
}

.week-swiper {
  height: 100rpx;
  width: 100%;
}

.month-swiper {
  width: 100%;
  height: 600rpx; // 调整高度以适应6行日期
}

.days-grid {
  &.week-mode {
    grid-template-rows: 1fr;
    height: 100rpx;
  }
}
</style>
